<?php

/**
 * SmartMySQL Base v1.3
 * http://www.phpclasses.org/smartmysql
 * 
 * Realised under the MIT license
 * http://www.opensource.org/licenses/mit-license.php
 * Copyright (c) Otar Chekurishvili (www.ottobyte.com)
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

class SmartMySQLBase {

	private $connection = null, $resource = null, $cacheType = null, $result = null;
	public $persistant = false;
	public $fetchMode = MYSQL_ASSOC;
	public $lastQuery = null;
	public $cache = false;
	public $cacheFolder = 'cache/';
	public $cacheExpire = 0;

	public function __construct($server, $username, $password, $database)
	{
		set_error_handler(array(__CLASS__, '_error'));
		$this->open($server, $username, $password, $database);
	}

	public function __destruct()
	{
		if ($this->persistant === false && $this->connection !== false && mysql_close($this->connection) === false) {
			trigger_error('Could not close database connection. Make sure nothing is wrong with link identifier resource.');	
		}
		$this->connection = $this->resource = $this->result = null;
		restore_error_handler();
	}

	private function open($server, $username, $password, $database)
	{
		$this->connection = $this->persistant === true ? @mysql_pconnect($server, $username, $password) : @mysql_connect($server, $username, $password);
		//TODO: Remove @ error killer before open functions (if possible)
		if ($this->connection === false) {
			trigger_error('Failed connection to &quot;' . $server . '&quot; server. Make sure that hostname, username and password are correct.');
		} elseif (mysql_select_db($database, $this->connection) === false) {
			trigger_error('Could not select &quot;' . $database . '&quot; database. Make sure that it exists.');
		}		
	}

	public function execute($query)
	{
		if (is_string($query) === false) {
			trigger_error('Could not execute operation. Query must be type of &quot;string&quot; to request.');
		}
		if (($this->resource = mysql_query($query, $this->connection)) === false) {
			trigger_error('Could not execute operation. Invalid query was processed, check your SQL syntax.');
		}
		$this->lastQuery = $query;
		return $this->resource;
	}

	public function fetch($query, $cache = 0)
	{
		return $this->_fetcher($query, $cache, __METHOD__);
	}

	public function fetchAll($query, $cache = 0)
	{
		return $this->_fetcher($query, $cache, __METHOD__, true);
	}

	public function escape($data)
	{
		//TODO: Add argument checking
		if (is_array($data)) {
			foreach (array_keys($data) as $item) {
				$item[] = $this->_escape($item);
			}
			return $item;
		}
		return $this->_escape($data);
	}

	private function _escape($string)
	{
		if (!is_int($string) || !is_numeric($string)) {
			$string = is_resource($this->connection) ? mysql_real_escape_string($string, $this->connection) : mysql_escape_string($string);
		}
		return $string;
	}

	private function _fetcher($query, $cache = 0, $cacheType, $loop = false)
	{
		if ($cache !== false && (($this->cache === true && $this->cacheExpire > 0) || $cache > 0)) {
			if ($cache > 0) {
				$this->cacheExpire = $cache;
			}
			$this->cacheType = $cacheType;
			$this->lastQuery = $query;
			if ($this->_cache('check') === true) {
				$this->result = $this->_cache('open');
			} else {
				$this->_fetcher($query, $loop);
				$this->_cache('save');
			}
		} else {
			return $this->_fetch($query, $loop);
		}
		return $this->result;
	}

	private function _fetch($query, $loop = false)
	{
		if ($this->execute($query) === false) {
			return false;
		}
		if ($loop === true ) {
			while ($r = mysql_fetch_array($this->resource, $this->fetchMode)) {
				$this->result[] = $r;
			}
		} else {
			$this->result = mysql_fetch_array($this->resource, $this->fetchMode);
		}
		return $this->result;
	}

	private function _cache($mode)
	{
		if ($this->cacheFolder == false) {
			trigger_error('Could not cache query. You must point writeable directory for storing cached files.');
		}
		$folder = $this->cacheFolder . date('Y-m-d') . '/';
		$filename = $folder . sha1($this->cacheType . $this->lastQuery) . '.cache';
		switch ($mode) {
			case 'check': return (file_exists($filename) === true && (filemtime($filename) > (time() - $this->cacheExpire))) ? true : false;
			case 'open': return unserialize(file_get_contents($filename));
			case 'save':
				if (is_dir($folder) === false) {
					if (mkdir($folder) === false) {
						trigger_error('Could not create &quot;' . $folder  . '&quot; caching sub-directory. Make sure that &quot;' . $this->cacheFolder . '&quot; cache directory exists.');
					}
				}
				return file_put_contents($filename, serialize($this->result));
		}
	}

	private static function _error($level = null, $message)
	{
		$error = "\n" . '<p style="margin: 5px 0; clear: both;"><strong>' . __CLASS__ . ' Error:</strong> ' . $message . '</p>' . "\n";
		exit($error);
	}

}

?>